package de.akabahn.ebd.stw.zdxfc.prs.block;

import static de.akabahn.ebd.dbd.SwitchPosition.LEFT;
import static de.akabahn.ebd.dbd.SwitchPosition.RIGHT;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BIT0;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BIT1;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BIT2;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BIT3;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BLOCK_1231;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BLOCK_1231_ANNONCE;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BLOCK_1231_MELDER_AN;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BLOCK_1231_MELDER_VL;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BLOCK_1231_SONNERIE;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BLOCK_1231_ZDM;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BLOCK_1231_ZDXFC;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.BLOCK_TESTZEIT;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.MELDER_SCHALTER_BLOCK1231_BLOC;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.MELDER_SCHALTER_S4880;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.MELDER_TASTE_BLOCK1231_ANN;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.MELDER_TASTE_BLOCK1231_RED;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.MELDER_TASTE_BLOCK1231_SN;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.MELDER_TASTE_BLOCK1231_TEST;
import static de.akabahn.ebd.stw.zdxfc.prs.ZDXFCConstants.SIGNAL_S4880;

import org.apache.log4j.Logger;

import de.akabahn.ebd.comm.dbd.DBDParameter;
import de.akabahn.ebd.dbd.SwitchPosition;
import de.akabahn.ebd.dbd.impl.DBDSignalD;
import de.akabahn.ebd.dbd.impl.DBDSignalF;
import de.akabahn.ebd.stw.zdxfc.prs.ZDXFCMain;
import de.akabahn.ebd.stw.zdxfc.prs.elemente.FahrwegElement;

/**
 * Implementierung der Blockschnittstelle zwischen Chateauprix und Martinstein.<br>
 * Das Blocksystem ist <code>BMVU</code> (Bloc manuelle a voie unique).
 * 
 * @author Peter Eimann
 * @since 1.0
 * @version 1.2
 */
public class Block1231 extends FahrwegElement {

	private final static Logger LOG = Logger.getLogger(Block1231.class);

	// Es gibt vier Werte (die zusammen verodert werden koennen):
	// 1 -> Vorblock (BIT0)
	// 2 -> Rueckblock (BIT1)
	// 4 -> Test-Anforderung (BIT2)
	// 8 -> Test-Bestaetigung (Positivmeldung) (BIT3)

	private boolean blockiert;
	private boolean vorblock;
	private boolean rueckblock;
	private boolean wiederholungsSperre;
	private boolean streckentastenSperre;
	private boolean semaphoreOffen;
	private boolean tasteGedruecktAnn;
	private boolean tasteGedruecktRed;
	private boolean tasteGedruecktSn;
	private long testZeit;
	
	private DBDSignalD signal12P5;
	private DBDSignalD signal12P6;
	private DBDSignalD signal12P7;
	private SwitchPosition weiche12W18;
	private SwitchPosition weiche12W19;
	private SwitchPosition weiche12W21;
	
	/**
	 * Initialisierung der Klasse, Setzen der Streckentastensperre auf aktiv<br>
	 * ID des Elements: <code>BLOCK_1231</code>
	 * 
	 * @param main Referenz auf die Hauptklasse
	 */
	public Block1231(ZDXFCMain main) {
		super(BLOCK_1231, main);
		streckentastenSperre = true;
		signal12P5 = new DBDSignalD();
		signal12P6 = new DBDSignalD();
		signal12P7 = new DBDSignalD();
	}

	/**
	 * Wird bei Aenderungen an der Blockschnittstelle von Martinstein vom <code>ZDXFCDispatcher</code> aufgerufen.<br>
	 * Aenderungen an der Blockschnittstelle selber:<br>
	 * <ol>
	 * <li>DBD-Wert 0: Aufruf von <code>rueckblockenVonZDM()</code></li>
	 * <li>DBD-Wert 1: Aufruf von <code>vorblockenVonZDM()</code></li>
	 * <li>DBD-Wert 4: Aufruf von <code>testeBlockVonZDM()</code></li>
	 * <li>DBD-Wert 8: Aufruf von <code>testOkVonZDM()</code></li>
	 * </ol>
	 * <br>
	 * Aenderungen an den ueberwachten Elementen von Martinstein (fuer die Annonce-Lampe):<br>
	 * <ol>
	 * <li>Aufruf von <code>pruefeAnnonceVonZDM()</code></li>
	 * </ol>
	 * 
	 * @param dbd Der geaenderte Wert aus dem DBD
	 * @see #rueckblockenVonZDM()
	 * @see #vorblockenVonZDM()
	 * @see #testeBlockVonZDM()
	 * @see #testOkVonZDM()
	 * @see #pruefeAnnonceVonZDM(DBDParameter)
	 */
	@Override
	public void onChangeProcess(DBDParameter dbd) {
		
		if (dbd.getName().equals(BLOCK_1231_ZDM)) {
			if (dbd.getValue() == 0) {
				rueckblockenVonZDM();
			} else if ((dbd.getValue() & BIT0) == BIT0) {
				vorblockenVonZDM();
			} else if ((dbd.getValue() & BIT2) == BIT2) {
				testeBlockVonZDM();
			} else if ((dbd.getValue() & BIT3) == BIT3) {
				testOkVonZDM();
			}
		} else if (dbd.getName().startsWith("12")) {
			pruefeAnnonceVonZDM(dbd);
		}
	}

	/**
	 * Ausgabe des Zustands der Blockschnittstelle
	 */
	public String toString() {
		return id + ": BL[" + blockiert + "] VB[" + vorblock + "] RB[" + rueckblock + "] WH[" + wiederholungsSperre
				  + "] ST[" + streckentastenSperre + "] SO[" + semaphoreOffen + "] TZ[" + testZeit
				  + "] S4880[" + new DBDSignalF(main.getSignal(SIGNAL_S4880).getSignalBegriff()).toString() + "]";
	}

	/**
	 * Wird aufgerufen, wenn das Streckengleis zwischen ZDM und ZDXFC freigefahren wird.<br>
	 * Wenn das Rueckblock-Flag von Martinstein gesetzt wurde, wird als Vorbedingung fuer den Rueckblock die
	 * Streckentastensperre inaktiv gesetzt, die Meldelampe der Block-Taste <code>SN</code> angeschaltet und
	 * der Sonnerie-Ton gestartet.
	 */
	public void streckenGleisFrei() {
		LOG.info(id + ": Streckengleis frei");
	
		if (rueckblock) {
			streckentastenSperre = false;
			main.getDbd().set(MELDER_TASTE_BLOCK1231_SN, BIT0);
			playSonnerieClip();
		}
		
		LOG.debug("Status " + toString());
	}

	/**
	 * Wird aufgerufen, wenn das Streckengleis zwischen ZDM und ZDXFC besetzt wird.<br>
	 * Loescht die Meldelampe <code>Voie libre</code>. Ist die Semaphore geoeffnet (<code>S4880</code> in
	 * Fahrtstellung), wird als Vorbedingung fuer den Vorblock die Semaphore geschlossen, die Wiederholungssperre auf 
	 * aktiv gesetzt, die Meldelampe der Block-Taste <code>SN</code> angeschaltet, die Meldelampe des Semaphoren-
	 * Schalters geloescht und der Sonnerie-Ton gestartet.
	 */
	public void streckenGleisBesetzt() {
		LOG.info(id + ": Streckengleis besetzt");
	
		main.getDbd().set(BLOCK_1231_MELDER_VL, 0);
		if (semaphoreOffen) {
			wiederholungsSperre = true;
			semaphoreOffen = false;
			main.getSignal(SIGNAL_S4880).stellen(DBDSignalF.S_BM);
			main.getDbd().set(MELDER_TASTE_BLOCK1231_SN, BIT0);
			main.getDbd().set(MELDER_SCHALTER_S4880, 0);
			playSonnerieClip();
		}
	
		LOG.debug("Status " + toString());
	}

	/**
	 * Wird durch den Tastendruck auf die Blocktaste <code>TEST</code> aufgerufen.<br>
	 * Wenn der Block in Grundstellung ist, wird das entsprechende Testflag in die DBD-Variable der Blockschnittstelle 
	 * geschrieben.
	 * 
	 * @see #isInGrundstellung()
	 */
	public void testeBlockNachZDM() {
		LOG.info(id + ": Teste Block nach ZDM");

		if (isInGrundstellung()) {
			main.getDbd().set(BLOCK_1231_ZDXFC, BIT2);
			main.getDbd().set(BLOCK_1231_ZDXFC, 0);
		}
		
		LOG.debug("Status " + toString());
	}

	/**
	 * Wird durch das Drehen des Blockschalters <code>Bloc</code> aufgerufen.<br>
	 * Setzt das Blockiert-Flag aktiv und schaltet die Meldelampe im entsprechenden Schalter an.
	 */
	public void sperreBlockVonZDM() {
		LOG.info(id + ": Sperre Block von ZDM");

		blockiert = true;
		main.getDbd().set(MELDER_SCHALTER_BLOCK1231_BLOC, BIT0);
		
		LOG.debug("Status " + toString());
	}
	
	/**
	 * Wird durch das Zurueckdrehen des Blockschalters <code>Bloc</code> aufgerufen.<br>
	 * Setzt das Blockiert-Flag inaktiv und loescht die Meldelampe im entsprechenden Schalter.
	 */
	public void entsperreBlockVonZDM() {
		LOG.info(id + ": Entsperre Block von ZDM");

		blockiert = false;
		main.getDbd().set(MELDER_SCHALTER_BLOCK1231_BLOC, 0);
		
		LOG.debug("Status " + toString());
	}
	
	/**
	 * Wird aufgerufen, wenn die Blocktaste <code>ANN</code> gedrueckt wird. Das Druecken der Taste wird gespeichert
	 * und <code>pruefeBlockaktion()</code> aufgerufen.
	 * 
	 * @see #pruefeBlockaktion()
	 */
	public void tasteGedruecktAnn() {
		LOG.info(id + ": Taste ANN gedrueckt");
		tasteGedruecktAnn = true;
		pruefeBlockaktion();
	}

	/**
	 * Wird aufgerufen, wenn die Blocktaste <code>ANN</code> losgelassen wird. Die Speicherung fuer das Druecken der
	 * Taste wird geloescht.
	 */
	public void tasteLosgelassenAnn() {
		LOG.info(id + ": Taste ANN losgelassen");
		tasteGedruecktAnn = false;
	}

	/**
	 * Wird aufgerufen, wenn die Blocktaste <code>RED</code> gedrueckt wird. Das Druecken der Taste wird gespeichert
	 * und <code>pruefeBlockaktion()</code> aufgerufen.
	 * 
	 * @see #pruefeBlockaktion()
	 */
	public void tasteGedruecktRed() {
		LOG.info(id + ": Taste RED gedrueckt");
		tasteGedruecktRed = true;
		pruefeBlockaktion();
	}

	/**
	 * Wird aufgerufen, wenn die Blocktaste <code>RED</code> losgelassen wird. Die Speicherung fuer das Druecken der
	 * Taste wird geloescht.
	 */
	public void tasteLosgelassenRed() {
		LOG.info(id + ": Taste RED losgelassen");
		tasteGedruecktRed = false;
	}

	/**
	 * Wird aufgerufen, wenn die Blocktaste <code>SN</code> gedrueckt wird. Das Druecken der Taste wird gespeichert,
	 * der Sonnerie-Ton gestoppt und <code>pruefeBlockaktion()</code> aufgerufen.
	 * 
	 * @see #pruefeBlockaktion()
	 */
	public void tasteGedruecktSn() {
		LOG.info(id + ": Taste SN gedrueckt");
		tasteGedruecktSn = true;
		main.getDbd().set(BLOCK_1231_SONNERIE, 0);
		pruefeBlockaktion();
	}
	
	/**
	 * Wird aufgerufen, wenn die Blocktaste <code>SN</code> losgelassen wird. Die Speicherung fuer das Druecken der
	 * Taste wird geloescht.
	 */
	public void tasteLosgelassenSn() {
		LOG.info(id + ": Taste SN losgelassen");
		tasteGedruecktSn = false;
	}
	
	/**
	 * Wird durch das Drehen des Semaphoren-Schalters aufgerufen.<br>
	 * Ist die Testzeit groesser 0 (positive Testquittung von Martinstein erhalten und Zeit noch nicht abgelaufen),
	 * wird die Semaphore <code>S4880</code> auf Fahrt gestellt, die Meldelampe im Schalter angeschaltet und die
	 * Testzeit auf 0 zurueckgesetzt.
	 */
	public void oeffneSemaphore() {
		LOG.info(id + ": Oeffne Semaphore");

		if (testZeit > 0) {
			setTestZeit(0);
			semaphoreOffen = true;
			main.getDbd().set(MELDER_SCHALTER_S4880, BIT0);
			main.getSignal(SIGNAL_S4880).stellen(DBDSignalF.VL);
		}
		
		LOG.debug("Status " + toString());
	}
	
	/**
	 * Wird durch das Zurueckdrehen des Semaphoren-Schalters aufgerufen.<br>
	 * Ist die Semaphore geoeffnet (<code>S4880</code> in Fahrtstellung), wird die Semaphore geschlossen und die 
	 * Meldelampe des Semaphoren-Schalters geloescht.
	 */
	public void schliesseSemaphore() {
		LOG.info(id + ": Schliesse Semaphore");

		if (semaphoreOffen) {
			semaphoreOffen = false;
			main.getSignal(SIGNAL_S4880).stellen(DBDSignalF.S_BM);
			main.getDbd().set(MELDER_SCHALTER_S4880, 0);
		}
	
		LOG.debug("Status " + toString());
	}
	
	/**
	 * Zugriff auf die aktuelle Testzeit.
	 * 
	 * @return Die aktuelle Testzeit
	 */
	public long getTestZeit() {
		return testZeit;
	}

	/**
	 * Setzt die Testzeit auf den uebergebenen Wert.<br>
	 * Ist die neue Zeit groesser <code>0</code>, wird die Meldelampe der Blocktaste <code>TEST</code> angeschaltet,
	 * ansonsten wird sie geloescht.
	 * 
	 * @param testZeit Die neue Testzeit
	 */
	public void setTestZeit(long testZeit) {
		this.testZeit = testZeit;

		if (testZeit > 0) {
			main.getDbd().set(MELDER_TASTE_BLOCK1231_TEST, BIT0);
		} else {
			main.getDbd().set(MELDER_TASTE_BLOCK1231_TEST, 0);
		}
	}

	/**
	 * Setzt den Block hilfweise wieder in Grundstellung zurueck (nicht vom Pult aus bedienbar, nur durch direktes
	 * Setzen der DBD-Variablen mit dem Wert 1 auszuloesen)
	 */
	public void hilfsGrundstellung() {
		LOG.warn(id + ": Hilfsgrundstellung");
		
		vorblock = false;
		rueckblock = false;
		wiederholungsSperre = false;
		streckentastenSperre = true;
		semaphoreOffen = false;
		setTestZeit(0);
		
		main.getDbd().set(BLOCK_1231_ZDXFC, 0);
		main.getDbd().set(MELDER_TASTE_BLOCK1231_SN, 0);
		main.getDbd().set(MELDER_TASTE_BLOCK1231_ANN, 0);
		main.getDbd().set(MELDER_TASTE_BLOCK1231_RED, 0);
		main.getDbd().set(MELDER_SCHALTER_S4880, 0);
		main.getSignal(SIGNAL_S4880).stellen(DBDSignalF.S_BM);
		main.getDbd().set(BLOCK_1231_MELDER_VL, BIT0);
		
		LOG.debug("Status " + toString());
	}
	
	/**
	 * Prueft, ob eine Zweitastenbedienung fuer den Vor- oder Rueckblock nach Martinstein vorliegt.<br>
	 * Ist die Taste <code>SN</code> und <code>ANN</code> gedrueckt, wird <code>vorblockenNachZDM()</code> aufgerufen, 
	 * ist die Taste <code>SN</code> und <code>RED</code> gedrueckt, wird <code>rueckblockenNachZDM()</code>
	 * aufgerufen. Bei einer Dreitastenbedienung wird keine Aktion ausgeloest.
	 * 
	 * @see #vorblockenNachZDM()
	 * @see #rueckblockenNachZDM()
	 */
	private void pruefeBlockaktion() {
		LOG.info(id + ": Pruefe Blockaktion");
		
		if (tasteGedruecktSn) {
			if (tasteGedruecktAnn && !tasteGedruecktRed) {
				vorblockenNachZDM();
			} else if (tasteGedruecktRed && !tasteGedruecktAnn) {
				rueckblockenNachZDM();
			}
		}
	}

	/**
	 * Blockt nach Martinstein vor, wenn bisher nicht vorgeblockt wurde und die Wiederholungssperre aktiv ist.<br>
	 * Setzt die Wiederholungssperre inaktiv, das Vorblock-Flag aktiv, schaltet die Meldelampe der Blocktaste
	 * <code>ANN</code>an, die Meldelampe der Blocktaste <code>SN</code> aus und setzt das Vorblock-Flag in der
	 * DBD-Variablen der Blockschnittstelle.
	 */
	private void vorblockenNachZDM() {
		LOG.info(id + ": Vorblocken nach ZDM");
		
		if (wiederholungsSperre && !vorblock) {
			main.getDbd().set(BLOCK_1231_ZDXFC, BIT0);
			main.getDbd().set(MELDER_TASTE_BLOCK1231_SN, 0);
			main.getDbd().set(MELDER_TASTE_BLOCK1231_ANN, BIT0);
			wiederholungsSperre = false;
			vorblock = true;
			LOG.info(id + ": Vorblocken OK");
		}

		LOG.debug("Status " + toString());
	}

	/**
	 * Blockt nach Martinstein zurueck, wenn das Rueckblock-Flag aktiv und die Streckentastensperre inaktiv ist.<br>
	 * Setzt das Rueckblock-Flag inaktiv, die Streckentastensperre aktiv, loescht die Meldelampen der Blocktasten
	 * <code>SN</code> und <code>RED</code>, schaltet die Meldelampe <code>Voie libre</code> und loescht das
	 * Rueckblock-Flag in der DBD-Variablen der Blockschnittstelle.
	 */
	private void rueckblockenNachZDM() {
		LOG.info(id + ": Rueckblocken nach ZDM");
	
		if (rueckblock && !streckentastenSperre) {
			main.getDbd().set(BLOCK_1231_ZDXFC, 0);
			main.getDbd().set(MELDER_TASTE_BLOCK1231_SN, 0);
			main.getDbd().set(MELDER_TASTE_BLOCK1231_RED, 0);
			main.getDbd().set(BLOCK_1231_MELDER_VL, BIT0);
			streckentastenSperre = true;
			rueckblock = false;
			LOG.info(id + ": Rueckblocken OK");
		}
	
		LOG.debug("Status " + toString());
	}

	/**
	 * Eingang des Vorblocks von Martinstein.<br>
	 * Das Rueckblock-Flag wird auf aktiv gesetzt, die Meldelampe im Blockschalter <code>RED</code> angeschaltet, die 
	 * Meldelampe <code>Voie libre</code> geloescht und das Rueckblock-Flag in der DBD-Variablen der Blockschnittstelle 
	 * gesetzt.
	 * 
	 * @see #isInGrundstellung()
	 */
	private void vorblockenVonZDM() {
		LOG.info(id + ": Vorblocken von ZDM");

		rueckblock = true;
		main.getDbd().set(BLOCK_1231_MELDER_VL, 0);
		main.getDbd().set(MELDER_TASTE_BLOCK1231_RED, BIT0);
		main.getDbd().set(BLOCK_1231_ZDXFC, BIT1);

		LOG.debug("Status " + toString());
	}
	
	/**
	 * Eingang des Rueckblocks von Martinstein.<br>
	 * Wenn vorher vorgeblock wurde, wird das Vorblock-Flag auf inaktiv gesetzt, die Meldelampe <code>Voie libre</code>
	 * angeschaltet, die Meldelampe im Blockschalter <code>RED</code> geloescht und das Vorblock-Flag in der DBD-
	 * Variablen der Blockschnittstelle geloescht.
	 */
	private void rueckblockenVonZDM() {
		LOG.info(id + ": Rueckblocken von ZDM");
		
		if (vorblock) {
			vorblock = false;
			main.getDbd().set(BLOCK_1231_MELDER_VL, BIT0);
			main.getDbd().set(MELDER_TASTE_BLOCK1231_ANN, 0);
			main.getDbd().set(BLOCK_1231_ZDXFC, 0);
		}

		LOG.debug("Status " + toString());
	}
	
	/**
	 * Testanforderung von Martinstein.<br>
	 * Wenn der Block in Grundstellung und nicht blockiert ist, wird die positive Testquittung in die DBD-Variable
	 * der Blockschnittstelle geschrieben.
	 * 
	 * @see #isInGrundstellung()
	 */
	private void testeBlockVonZDM() {
		LOG.info(id + ": Teste Block von ZDM");
		
		if (isInGrundstellung() && !blockiert) {
			main.getDbd().set(BLOCK_1231_ZDXFC, BIT3);
			main.getDbd().set(BLOCK_1231_ZDXFC, 0);
		}

		LOG.debug("Status " + toString());
	}

	/**
	 * Eingang der positiven Testquittung von Martinstein nach der Testanforderung von Chateauprix.<br>
	 * Wenn der Block in Grundstellung ist, wird die entsprechende Testzeit eingestellt (aktuelle Zeit plus Konstante
	 * <code>BLOCK_TESTZEIT</code>).
	 * 
	 * @see #isInGrundstellung()
	 * @see #setTestZeit(long)
	 */
	private void testOkVonZDM() {
		LOG.info(id + ": Test OK von ZDM");

		if (isInGrundstellung()) {
			setTestZeit(System.currentTimeMillis() + BLOCK_TESTZEIT);
		}

		LOG.debug("Status " + toString());
	}
	
	/**
	 * Prueft, ob der Block in Grundstellung ist:<br>
	 * <ol>
	 * <li>Vorblock-Flag ist inaktiv</li>
	 * <li>Ruckblock-Flag ist inaktiv</li>
	 * <li>Wiederholungssperre ist inaktiv</li>
	 * <li>Streckentastensperre ist aktiv</li>
	 * <li>Semaphore <code>S4880</code> ist geschlossen</li>
	 * <li>Testzeit ist 0</li>
	 * <li>Semaphore <code>S4880</code> zeigt Halt (S)</li>
	 * </ol>
	 * 
	 * @return <code>true</code> - Block ist in Grundstellung
	 */
	private boolean isInGrundstellung() {
		boolean grundStellung = !vorblock 
							 && !rueckblock 
							 && !wiederholungsSperre 
							 && streckentastenSperre 
							 && !semaphoreOffen 
							 && testZeit == 0 
							 && main.getSignal(SIGNAL_S4880).isHalt();
		
		LOG.debug("Status " + toString());
		LOG.info(id + ": Grundstellung = " + grundStellung);
		return grundStellung;
	}
	
	/**
	 * Speichert den geaenderten Wert der ueberwachten Elemente von Martinstein und prueft anhand ihrer Werte,
	 * ob die Annonce-Lampe eingeschaltet werden muss.<br>
	 * Fuer die Einschaltung gelten folgende Bedingungen:<br>
	 * <ol>
	 * <li>Signal 12P7 zeigt Ks1 <b>oder</b></li>
	 * <li>Signal 12P6 zeigt Ks1 und Weiche 21 liegt links <b>oder</b></li>
	 * <li>Signal 12P6 zeigt Ks1 und Weiche 21 liegt rechts und Weichen 19 und 18 liegen links <b>oder</b></li>
	 * <li>Signal 12P5 zeigt Ks1 und Weichen 19 und 18 liegen links</li>
	 * </ol>
	 * Beim Einschalten der Annonce-Lampe wird auch der Annonce-Ton abgespielt.
	 * 
	 * @param dbd Der geaenderte Wert aus dem DBD
	 */
	private void pruefeAnnonceVonZDM(DBDParameter dbd) {
		if ("12P5S".equalsIgnoreCase(dbd.getName())) {
			signal12P5.setIntValue(dbd.getValue());
		} else if ("12P6S".equalsIgnoreCase(dbd.getName())) {
			signal12P6.setIntValue(dbd.getValue());
		} else if ("12P7S".equalsIgnoreCase(dbd.getName())) {
			signal12P7.setIntValue(dbd.getValue());
		} else if ("12W18I".equalsIgnoreCase(dbd.getName())) {
			weiche12W18 = SwitchPosition.fromValue(dbd.getValue());
		} else if ("12W19I".equalsIgnoreCase(dbd.getName())) {
			weiche12W19 = SwitchPosition.fromValue(dbd.getValue());
		} else if ("12W21I".equalsIgnoreCase(dbd.getName())) {
			weiche12W21 = SwitchPosition.fromValue(dbd.getValue());
		}
		
		LOG.debug("12P5(" + signal12P5.toString() + "), "
			+ "12P6(" + signal12P6.toString() + "), "
			+ "12P7(" + signal12P7.toString() + "), "
			+ "12W18(" + weiche12W18.name() + "), "
			+ "12W19(" + weiche12W19.name() + "), "
			+ "12W21(" + weiche12W21.name() + ")");
		
		if (signal12P7.isKs1()
			|| (signal12P6.isKs1() && weiche12W21 == LEFT)
			|| (signal12P6.isKs1() && weiche12W21 == RIGHT && weiche12W19 == LEFT && weiche12W18 == LEFT)
			|| (signal12P5.isKs1() && weiche12W19 == LEFT && weiche12W18 == LEFT)) {
			main.getDbd().set(BLOCK_1231_MELDER_AN, BIT0);
			playAnnonceClip();
		}
	}
	
	/**
	 * Spielt den Annonce-Ton ab
	 */
	private void playAnnonceClip() {
		main.getDbd().set(BLOCK_1231_ANNONCE, 1);
	}

	/**
	 * Spielt den Sonnerie-Ton in einer Endlosschleife ab
	 */
	private void playSonnerieClip() {
		main.getDbd().set(BLOCK_1231_SONNERIE, 1);
	}
}
